/*********************************************************************************
* Copyright (c) zhujinyang.
* All rights reserved.
*
* \file
*
* \system debug printf
*
* 
*********************************************************************************/
#include "cc_include.h"
#include "cc_fifo.h"
#include "hal_uart_drv.h"
#include "hal_nocache_drv.h"
#include "g_cfg.h"
#include "module.h"


/*
********************************************************************************
* define config parameters
********************************************************************************
*/
#define LOG_BUF_SIZE   (1024)
#define DEBUG_BUAD     (115200)
#define SEND_BUF_SIZE  128




/*
********************************************************************************
* define struct
********************************************************************************
*/
#define os_port_reset()  \
do {  \
   *(volatile INT32U *)(0xE000ED00 + 12) = 0x05FA0004;  \
    for(; ;);  \
} while (0)



/*
********************************************************************************
* define module variants
********************************************************************************
*/
static CC_FIFO_T  s_fifo;
static INT32U     s_log_buf[LOG_BUF_SIZE / 4];
static INT8U     *s_sendbuf;

static const char s_asiic_char[] = "0123456789ABCDEF";


/**********************************************************************************
* \brief:       Integer to hexadecimal character conversion
* \param[in]:   none
* \return:      none
**********************************************************************************/
static INT8U *itoa_hex(INT8U *s, INT32U val)
{
    INT8U i, ch[8], *c = &ch[8];

    do {
        *(--c) = s_asiic_char[val % 16];
    } while ((val = val / 16) > 0);

    for (i = 0; i < &ch[8] - c; i++) {
        *s++ = c[i];
    }
    
    return s;
}

/**********************************************************************************
* \brief:       Integer to decimal character conversion
* \param[in]:   none
* \return:      none
**********************************************************************************/
static INT8U *itoa_dec(INT8U *s, INT32U val)
{
    INT8U i, ch[10], *c = &ch[10];

    do {
        *(--c) = s_asiic_char[val % 10];
    } while ((val = val / 10) > 0);

    for (i = 0; i < &ch[10] - c; i++) {
        *s++ = c[i];
    }
    
    return s;
}

/**********************************************************************************
* \brief:       error callback
* \param[in]:   none
* \return:      none
**********************************************************************************/
static void err_handler(char *filename, INT32U line)
{
    os_port_reset();
    for(; ;);
}


/**********************************************************************************
* \brief:       log output/store processing general entry
* \param[in]:   none
* \return:      none
**********************************************************************************/
static void log_task_entry(void *pdata)
{
    INT32U readlen, delay;
    
    for (; ;) {
        readlen = cc_fifo_readdata(&s_fifo, s_sendbuf, SEND_BUF_SIZE);
        if (readlen > 0) {
            hal_uart_send_data(UART_COM_1, s_sendbuf, readlen);
            delay = readlen / (DEBUG_BUAD / 10 / 1000) + 2;
        } else {
            delay = 20;
        }
        os_thread_sleep(delay);
    }
}

/**********************************************************************************
* \brief:       printf
* \param[in]:   fmt:     string to be output
* \return:      > 0: success
**********************************************************************************/
INT32S printf_com(const char *fmt, ...)
{
    INT32U *sp;
    INT32S  temp;
    INT8U   sendbuf[64], *s = sendbuf;

    #if 0
    *s++ = '[';
    s = itoa_dec(s, os_sys_tick_get_time());
    *s++ = ']';
    #endif
    
    sp = (INT32U *)&fmt;
    for ( ; *fmt; ) {
        if (*fmt != '%') {
            *s++ = *fmt++;
            continue;
        }

        fmt++;
        sp++;
        switch(*fmt++) {
        case 's': case 'S': {
            char *str = (char *)(*sp);
            do {
                *s++ = *str++;
            } while(*str);
            }
            break;

        case 'c': case 'C':
            *s++ = (char)(*sp);
            break;
        
        case 'u': case 'U':
            s = itoa_dec(s, (INT32U)(*sp));
            break;
            
        case 'x': case 'X':
            s = itoa_hex(s, (INT32U)(*sp));
            break;
            
        case 'd': case 'D':
            temp = (INT32S)(*sp);
            if (temp < 0) {
                *s++ = '-';
                temp = -temp;
            }
            s = itoa_dec(s,  temp);
            break;

        case 'f': case 'F': {
            FP64 fp;
            INT32U cnt;
            
            /* The correctness needs to be confirmed */
            if (((INT32U)&fmt & 0x07) == 0) {
                if ((INT32U)sp & 0x07) {
                    sp++;
                }
            } else {
                if (((INT32U)sp & 0x07) == 0) {
                    sp++;
                }
            }
            
            fp = *((FP64 *)sp);
            if (fp < 0) {
                *s++ = '-';
                fp = -fp;
            }
            
            temp = (INT32U)fp;
            s = itoa_dec(s,  temp);
            *s++ = '.';
            temp = (fp - temp) * 1000001;
            if (temp < 10) {
                cnt = 5;
            } else if (temp < 100) {
                cnt = 4;
            } else if (temp < 1000) {
                cnt = 3;
            } else if (temp < 10000) {
                cnt = 2;
            } else if (temp < 100000) {
                cnt = 1;
            } else {
                cnt = 0;
            }

            for (; cnt > 0; cnt--) {
                *s++ = '0';
            }
            s = itoa_dec(s,  temp);
            sp++;
            }
            break;
            
        default:
            break;
        }
    }

    CC_ASSERT((s - sendbuf) <= sizeof(sendbuf));

    return cc_fifo_writedata(&s_fifo, sendbuf, (s - sendbuf));
}




/**********************************************************************************
* \brief:       printf
* \param[in]:   fmt:     string to be output
* \return:      > 0: success
**********************************************************************************/
INT8U *printf_buffer(INT8U *buffer, const char *fmt, ...)
{
    INT32U *sp;
    INT32S  temp;
    INT8U  *s = buffer;

    #if 0
    *s++ = '[';
    s = itoa_dec(s, os_sys_tick_get_time());
    *s++ = ']';
    #endif
    
    sp = (INT32U *)&fmt;
    for ( ; *fmt; ) {
        if (*fmt != '%') {
            *s++ = *fmt++;
            continue;
        }

        fmt++;
        sp++;
        switch(*fmt++) {
        case 's': case 'S': {
            char *str = (char *)(*sp);
            do {
                *s++ = *str++;
            } while(*str);
            }
            break;

        case 'c': case 'C':
            *s++ = (char)(*sp);
            break;
        
        case 'u': case 'U':
            s = itoa_dec(s, (INT32U)(*sp));
            break;
            
        case 'x': case 'X':
            s = itoa_hex(s, (INT32U)(*sp));
            break;
            
        case 'd': case 'D':
            temp = (INT32S)(*sp);
            if (temp < 0) {
                *s++ = '-';
                s = itoa_dec(s, -temp);
            } else {
                s = itoa_dec(s,  temp);
            }
            break;

        case 'f': case 'F': {
            FP64 fp;
            INT32U cnt;
            
            /* The correctness needs to be confirmed */
            if (((INT32U)&fmt & 0x07) == 0) {
                if ((INT32U)sp & 0x07) {
                    sp++;
                }
            } else {
                if (((INT32U)sp & 0x07) == 0) {
                    sp++;
                }
            }
            
            fp = *((FP64 *)sp);
            if (fp < 0) {
                *s++ = '-';
                fp = -fp;
            }
            
            temp = (INT32U)fp;
            s = itoa_dec(s,  temp);
            *s++ = '.';
            temp = (fp - temp) * 1000001;
            if (temp < 10) {
                cnt = 5;
            } else if (temp < 100) {
                cnt = 4;
            } else if (temp < 1000) {
                cnt = 3;
            } else if (temp < 10000) {
                cnt = 2;
            } else if (temp < 100000) {
                cnt = 1;
            } else {
                cnt = 0;
            }

            for (; cnt > 0; cnt--) {
                *s++ = '0';
            }
            s = itoa_dec(s,  temp);
            sp++;
            }
            break;
            
        default:
            break;
        }
    }

    return s;
}


/**********************************************************************************
* \brief:       printf hexadec
* \param[in]:   data:     data pointer
* \param[in]:   datalen:     data length
* \return:      > 0: success
**********************************************************************************/
INT32S printf_hexadec(INT8U *data, INT32U datalen)
{
    INT32U       i; 
    INT8U        sendbuf[40][3];

    if (datalen >= 40) {
        datalen = (40 - 1);
    }
    
    for (i = 0; i < datalen; i++) {
        sendbuf[i][0] = s_asiic_char[(data[i] >> 4) & 0x0F];
        sendbuf[i][1] = s_asiic_char[data[i] & 0x0F];
        sendbuf[i][2] = ' ';
    }
    
    sendbuf[i][0] = '\n';
    
    return cc_fifo_writedata(&s_fifo, sendbuf[0], (i * 3) + 1);
}

/**********************************************************************************
* \brief:       asiic to int
* \param[in]:   fmt:     string to be output
* \return:      > 0: success
**********************************************************************************/
INT32S printf_atoi(const char *str)
{
    INT32S n = 1, result = 0;

    if (*str == '-') {
        n = -1;
        str++;
    }
    
    while (*str) {
        if (*str <= '9' && *str >= '0') {
            result *= 10;
            result += (*str - '0');
        }
        str++;
    }

    return result * n;
}


/**********************************************************************************
* \brief:       printf
* \param[in]:   fmt:     string to be output
* \return:      > 0: success
**********************************************************************************/
INT32S printf_com_ex(const char *fmt, ...)
{
    INT32U *sp;
    INT32S  temp;
    INT8U   sendbuf[64], *s = sendbuf;

    sp = (INT32U *)&fmt;
    for ( ; *fmt; ) {
        if (*fmt != '%') {
            *s++ = *fmt++;
            continue;
        }

        fmt++;
        sp++;
        switch(*fmt++) {
        case 's': case 'S': {
            char *str = (char *)(*sp);
            do {
                *s++ = *str++;
            } while(*str);
            }
            break;

        case 'c': case 'C':
            *s++ = (char)(*sp);
            break;
        
        case 'u': case 'U':
            s = itoa_dec(s, (INT32U)(*sp));
            break;
            
        case 'x': case 'X':
            s = itoa_hex(s, (INT32U)(*sp));
            break;
            
        case 'd': case 'D':
            temp = (INT32S)(*sp);
            if (temp < 0) {
                *s++ = '-';
                s = itoa_dec(s, -temp);
            } else {
                s = itoa_dec(s,  temp);
            }
            break;

        case 'f': case 'F': {
            FP64 fp;
            INT32U cnt;
            
            /* The correctness needs to be confirmed */
            if (((INT32U)&fmt & 0x07) == 0) {
                if ((INT32U)sp & 0x07) {
                    sp++;
                }
            } else {
                if (((INT32U)sp & 0x07) == 0) {
                    sp++;
                }
            }
            
            fp = *((FP64 *)sp);
            if (fp < 0) {
                *s++ = '-';
                fp = -fp;
            }
            
            temp = (INT32U)fp;
            s = itoa_dec(s,  temp);
            *s++ = '.';
            temp = (fp - temp) * 1000001;
            if (temp < 10) {
                cnt = 5;
            } else if (temp < 100) {
                cnt = 4;
            } else if (temp < 1000) {
                cnt = 3;
            } else if (temp < 10000) {
                cnt = 2;
            } else if (temp < 100000) {
                cnt = 1;
            } else {
                cnt = 0;
            }

            for (; cnt > 0; cnt--) {
                *s++ = '0';
            }
            s = itoa_dec(s,  temp);
            sp++;
            }
            break;
            
        default:
            break;
        }
    }

    CC_ASSERT((s - sendbuf) <= sizeof(sendbuf));

    return cc_fifo_writedata(&s_fifo, sendbuf, (s - sendbuf));
}


/**********************************************************************************
* \brief:       print init
* \param[in]:   none
* \return:      none
**********************************************************************************/
void mal_printf_init(void)
{
    INT32S  ret;
    void   *tid;
    
    ret = hal_uart_open_channel(UART_COM_1, 8, 1,  0, DEBUG_BUAD);
    CC_ASSERT(ret == 0);

    cc_fifo_create(&s_fifo, (INT8U *)s_log_buf, sizeof(s_log_buf));
    tid = os_thread_create(-1, log_task_entry, 0, 0, 1, 0);
    CC_ASSERT(tid);

    s_sendbuf = hal_nocache_get_addr(SEND_BUF_SIZE);

    cc_error_register_handler(err_handler);
}

MAL_MODULE_INIT(0, mal_printf_init);

